package com.wlyuan.gateway.security;


import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;

/**
 * @author yuanzheng
 */
@Slf4j
public class ResourceAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
    private final AntPathMatcher excludePathMatcher = new AntPathMatcher();
    private final Set<String> excludePatterns = new ConcurrentSkipListSet<>();


    public ResourceAuthorizationManager() {
        excludePatterns.add("/**/v2/api-docs/**");
        excludePatterns.add("/**/swagger-resources/**");
        excludePatterns.add("/swagger-ui/**");
        excludePatterns.add("/swagger-resources/**");
        excludePatterns.add("/webjars/**");
    }

    private boolean isIgnoreUrl(String requestPath) {
        return excludePatterns.stream()
                .anyMatch(pattern -> excludePathMatcher.match(pattern, requestPath));
    }

    /**
     * 实现权限验证判断
     */
    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authenticationMono, AuthorizationContext authorizationContext) {
        ServerWebExchange exchange = authorizationContext.getExchange();
        //请求资源
        String requestPath = exchange.getRequest().getURI().getPath();
        // 是否直接放行
        if (isIgnoreUrl(requestPath)) {
            return Mono.just(new AuthorizationDecision(true));
        }

        return authenticationMono.filter(Authentication::isAuthenticated)
                .map(auth -> {
                    boolean authorized = checkAuthorities(exchange, auth, requestPath);
                    return new AuthorizationDecision(authorized);
                }).defaultIfEmpty(new AuthorizationDecision(false));
    }


    private boolean checkAuthorities(ServerWebExchange exchange, Authentication auth, String requestPath) {
        // 在这里可以检查用户是否有权限访问url
        if (auth instanceof OAuth2Authentication) {
            OAuth2Authentication authentication = (OAuth2Authentication) auth;
            String clientId = authentication.getOAuth2Request().getClientId();
            logger.info("Authentication: {}={}", clientId, JSON.toJSON(authentication));
        }
        return true;
    }
}
